home *** CD-ROM | disk | FTP | other *** search
- /* sgitopnm.c - read an SGI image and and produce a portable anymap
- **
- ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
- **
- ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
- ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
- **
- ** Permission to use, copy, modify, and distribute this software and its
- ** documentation for any purpose and without fee is hereby granted, provided
- ** that the above copyright notice appear in all copies and that both that
- ** copyright notice and this permission notice appear in supporting
- ** documentation. This software is provided "as is" without express or
- ** implied warranty.
- **
- ** 29Jan94: first version
- ** 08Feb94: minor bugfix
- ** 12Jun94: avoid fseek() if possible, fix for images with >3 channels
- */
- #include "pnm.h"
- #include "sgi.h"
-
- /*#define DEBUG*/
-
- #ifndef SEEK_SET
- #define SEEK_SET 0
- #endif
-
-
- /* entry in RLE offset table */
- typedef struct {
- long start; /* offset in file */
- long length; /* length of compressed scanline */
- } TabEntry;
-
- typedef short ScanElem;
- typedef ScanElem * ScanLine;
-
- /* prototypes */
- static unsigned char get_byte ARGS(( FILE* f ));
- static long get_big_long ARGS((FILE *f));
- static short get_big_short ARGS((FILE *f));
- static short get_byte_as_short ARGS((FILE *f));
- static void readerr ARGS((FILE *f));
- static void * xmalloc ARGS((int bytes));
- #define MALLOC(n, type) (type *)xmalloc((n) * sizeof(type))
- static char * compression_name ARGS((char compr));
- static void read_bytes ARGS((FILE *ifp, int n, char *buf));
- static Header * read_header ARGS((FILE *ifp));
- static TabEntry * read_table ARGS((FILE *ifp, int tablen));
- static ScanLine * read_channels ARGS((FILE *ifp, Header *head, TabEntry *table, short (*func) ARGS((FILE *)) ));
- static void image_to_pnm ARGS((Header *head, ScanLine *image, xelval maxval));
- static void rle_decompress ARGS((ScanElem *src, int srclen, ScanElem *dest, int destlen));
-
- #define WORSTCOMPR(x) (2*(x) + 2)
-
-
- static short verbose = 0;
-
-
- int
- main(argc, argv)
- int argc;
- char *argv[];
- {
- FILE *ifp;
- int argn;
- char *usage = "[-verbose] [sgifile]";
- TabEntry *table = NULL;
- ScanLine *image;
- Header *head;
- long maxval;
-
- pnm_init(&argc, argv);
-
- argn = 1;
- while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
- if( pm_keymatch(argv[argn], "-verbose", 2) )
- verbose++;
- else
- if( pm_keymatch(argv[argn], "-noverbose", 4) )
- verbose = 0;
- else
- pm_usage(usage);
- ++argn;
- }
-
- if( argn < argc ) {
- ifp = pm_openr( argv[argn] );
- argn++;
- }
- else
- ifp = stdin;
-
- if( argn != argc )
- pm_usage(usage);
-
- head = read_header(ifp);
- maxval = head->pixmax - head->pixmin;
- if( maxval > PNM_MAXMAXVAL )
- pm_error("pixel values too large - try reconfiguring with PGM_BIGGRAYS\n or without PPM_PACKCOLORS");
-
- if( head->storage != STORAGE_VERBATIM )
- table = read_table(ifp, head->ysize * head->zsize);
- if( head->bpc == 1 )
- image = read_channels(ifp, head, table, get_byte_as_short);
- else
- image = read_channels(ifp, head, table, get_big_short);
-
- image_to_pnm(head, image, (xelval)maxval);
- pm_close(ifp);
-
- exit(0);
- }
-
-
- static Header *
- read_header(ifp)
- FILE *ifp;
- {
- Header *head;
-
- head = MALLOC(1, Header);
-
- head->magic = get_big_short(ifp);
- head->storage = get_byte(ifp);
- head->bpc = get_byte(ifp);
- head->dimension = get_big_short(ifp);
- head->xsize = get_big_short(ifp);
- head->ysize = get_big_short(ifp);
- head->zsize = get_big_short(ifp);
- head->pixmin = get_big_long(ifp);
- head->pixmax = get_big_long(ifp);
- read_bytes(ifp, 4, head->dummy1);
- read_bytes(ifp, 80, head->name);
- head->colormap = get_big_long(ifp);
- read_bytes(ifp, 404, head->dummy2);
-
- if( head->magic != SGI_MAGIC )
- pm_error("bad magic number - not an SGI image");
- if( head->storage < 0 || head->storage > 1 )
- pm_error("unknown compression type");
- if( head->bpc < 1 || head->bpc > 2 )
- pm_error("illegal precision value %d (only 1-2 allowed)", head->bpc );
- if( head->colormap != CMAP_NORMAL )
- pm_error("unsupported non-normal pixel data");
- if( head->zsize == 0 )
- pm_error("illegal channel (zsize) value 0");
-
- if( verbose ) {
- pm_message("raster size %dx%d, %d channels", head->xsize, head->ysize, head->zsize);
- pm_message("compression: %d = %s", head->storage, compression_name(head->storage));
- head->name[79] = '\0'; /* just to be safe */
- pm_message("Image name: \"%s\"", head->name);
- #ifdef DEBUG
- pm_message("bpc: %d dimension: %d", head->bpc, head->dimension);
- pm_message("pixmin: %ld pixmax: %ld colormap: %ld", head->pixmin, head->pixmax, head->colormap);
- #endif
- }
-
- /* check ysize/zsize & dimension, just to be sure */
- again:
- switch( head->dimension ) {
- case 1:
- if( head->zsize != 1 ) {
- pm_message("1-dimensional image with %d channels ?!", head->zsize);
- pm_message("trying to interpret this as a 3-dimensional image...");
- head->dimension = 3;
- goto again;
- }
- if( head->ysize == 1 )
- break; /* ok */
- pm_message("1-dimensional image with ysize %d ?!", head->ysize);
- pm_message("trying to interpret this as a 2-dimensional image...");
- head->dimension = 2;
- /* fall through */
- case 2:
- if( head->zsize == 1 )
- break; /* ok */
- pm_message("2-dimensional image with %d channels ?!", head->zsize);
- pm_message("trying to interpret this as a 3-dimensional image...");
- head->dimension = 3;
- /* fall through */
- case 3:
- switch( head->zsize ) {
- case 1:
- case 2:
- pm_message("3-dimensional image with %d channels ?!", head->zsize);
- pm_message("interpreting this image as greyscale");
- /*head->dimension = 2;*/
- break;
- case 3:
- break;
- default:
- pm_message("%d-channel image, using only first 3 channels", head->zsize);
- /*head->zsize = 3;*/
- break;
- }
- break;
- default:
- pm_error("illegal dimension value %d (only 1-3 allowed)", head->dimension);
- }
-
- return head;
- }
-
-
- static TabEntry *
- read_table(ifp, tablen)
- FILE *ifp;
- int tablen;
- {
- TabEntry *table;
- int i;
-
- table = MALLOC(tablen, TabEntry);
-
- #ifdef DEBUG
- pm_message("reading offset table");
- #endif
-
- for( i = 0; i < tablen; i++ )
- table[i].start = get_big_long(ifp);
- for( i = 0; i < tablen; i++ )
- table[i].length = get_big_long(ifp);
-
- return table;
- }
-
-
-
- static ScanLine *
- read_channels(ifp, head, table, func)
- FILE *ifp;
- Header *head;
- TabEntry *table;
- short (*func) ARGS((FILE *));
- {
- ScanLine *image;
- ScanElem *temp;
- int channel, maxchannel, row, sgi_index, i;
- long offset, length;
-
- #ifdef DEBUG
- pm_message("reading channels");
- #endif
-
- maxchannel = head->zsize;
- if( maxchannel < 3 )
- maxchannel = 1;
- else if( maxchannel > 3 )
- maxchannel = 3;
- image = MALLOC(head->ysize * maxchannel, ScanLine);
- if( table ) temp = MALLOC(WORSTCOMPR(head->xsize), ScanElem);
-
- for( channel = 0; channel < maxchannel; channel++ ) {
- #ifdef DEBUG
- pm_message(" channel %d", channel);
- #endif
- for( row = 0; row < head->ysize; row++ ) {
- sgi_index = channel * head->ysize + row;
- image[sgi_index] = MALLOC(head->xsize, ScanElem);
- if( table ) {
- offset = table[sgi_index].start;
- length = table[sgi_index].length;
- if( head->bpc == 2 )
- length /= 2; /* length is in bytes, we are reading words */
- if( length > WORSTCOMPR(head->xsize) )
- pm_error("corrupted start/length table for channel %d scanline %d ??", channel, row);
- if( ftell(ifp) != offset ) { /* avoid unnescessary fseek() */
- if( fseek(ifp, offset, SEEK_SET) != 0 )
- pm_error("seek error for offset %ld - channel %d scanline %d", offset, channel, row);
- }
- for( i = 0; i < length; i++ )
- temp[i] = (*func)(ifp);
- rle_decompress(temp, length, image[sgi_index], head->xsize);
- }
- else {
- for( i = 0; i < head->xsize; i++ )
- image[sgi_index][i] = (*func)(ifp);
- }
- }
- }
-
- if( table ) free(temp);
- return image;
- }
-
-
- static void
- image_to_pnm(head, image, maxval)
- Header *head;
- ScanLine *image;
- xelval maxval;
- {
- int col, row, format;
- xel *pnmrow = pnm_allocrow(head->xsize);
- int sub = head->pixmin;
-
- if( head->zsize < 3 ) {
- pm_message("writing PGM image");
- format = PGM_TYPE;
- }
- else {
- pm_message("writing PPM image");
- format = PPM_TYPE;
- }
-
- pnm_writepnminit(stdout, head->xsize, head->ysize, (xelval)maxval, format, 0);
- for( row = head->ysize-1; row >= 0; row-- ) {
- for( col = 0; col < head->xsize; col++ ) {
- if( format == PGM_TYPE )
- PNM_ASSIGN1(pnmrow[col], image[row][col] - sub);
- else {
- pixval r, g, b;
- r = image[row][col] - sub;
- g = image[head->ysize + row][col] - sub;
- b = image[2* head->ysize + row][col] - sub;
- PPM_ASSIGN(pnmrow[col], r, g, b);
- }
- }
- pnm_writepnmrow(stdout, pnmrow, head->xsize, (xelval)maxval, format, 0);
- }
- pnm_freerow(pnmrow);
- }
-
-
- static void
- rle_decompress(src, srcleft, dest, destleft)
- ScanElem *src;
- int srcleft;
- ScanElem *dest;
- int destleft;
- {
- int count;
- unsigned char el;
-
- while( srcleft ) {
- el = (unsigned char)(*src++ & 0xff);
- --srcleft;
- count = (int)(el & 0x7f);
-
- if( count == 0 )
- return;
- if( destleft < count )
- pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count);
- destleft -= count;
- if( el & 0x80 ) {
- if( srcleft < count )
- pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count);
- srcleft -= count;
- while( count-- )
- *dest++ = *src++;
- }
- else {
- if( srcleft == 0 )
- pm_error("RLE error: not enough data for replicate run");
- while( count-- )
- *dest++ = *src;
- ++src;
- --srcleft;
- }
- }
- pm_error("RLE error: no terminating 0-byte");
- }
-
-
- /* basic I/O functions, taken from ilbmtoppm.c */
-
- static short
- get_big_short(ifp)
- FILE *ifp;
- {
- short s;
-
- if( pm_readbigshort(ifp, &s) == -1 )
- readerr(ifp);
-
- return s;
- }
-
- static long
- get_big_long(ifp)
- FILE *ifp;
- {
- long l;
-
- if( pm_readbiglong(ifp, &l) == -1 )
- readerr(ifp);
-
- return l;
- }
-
- static unsigned char
- get_byte(ifp)
- FILE* ifp;
- {
- int i;
-
- i = getc(ifp);
- if( i == EOF )
- readerr(ifp);
-
- return (unsigned char) i;
- }
-
-
- static void
- readerr(f)
- FILE *f;
- {
- if( ferror(f) )
- pm_error("read error");
- else
- pm_error("premature EOF");
- }
-
-
- static void
- read_bytes(ifp, n, buf)
- FILE *ifp;
- int n;
- char *buf;
- {
- int r;
-
- r = fread((void *)buf, 1, n, ifp);
- if( r != n )
- readerr(ifp);
- }
-
-
- static short
- get_byte_as_short(ifp)
- FILE *ifp;
- {
- return (short)get_byte(ifp);
- }
-
-
- static void *
- xmalloc(bytes)
- int bytes;
- {
- void *mem;
-
- if( bytes == 0 )
- return NULL;
-
- mem = malloc(bytes);
- if( mem == NULL )
- pm_error("out of memory allocating %d bytes", bytes);
- return mem;
- }
-
-
- static char *
- compression_name(compr)
- char compr;
- {
- switch( compr ) {
- case STORAGE_VERBATIM:
- return "none";
- case STORAGE_RLE:
- return "RLE";
- default:
- return "unknown";
- }
- }
-
-